import 'dart:async';
import 'dart:io';

import 'package:get/get.dart';
import 'package:winkai/ext/ext.dart';
import 'package:winkai/helper/iap/iap_api.dart';
import 'package:winkai/helper/iap/payment_queue_delegate.dart';
import 'package:winkai/helper/value_helper.dart';
import 'package:winkai/utils/package_info_utils.dart';
import 'package:winkai/utils/page_jump_utils.dart';
import 'package:winkai/utils/smart_dialog.dart';
import 'package:in_app_purchase/in_app_purchase.dart';
import 'package:in_app_purchase_android/billing_client_wrappers.dart';
import 'package:in_app_purchase_android/in_app_purchase_android.dart';
import 'package:in_app_purchase_storekit/in_app_purchase_storekit.dart';
import 'package:in_app_purchase_storekit/store_kit_wrappers.dart';

import '../../entity/subscribe_from.dart';
import 'entity/a_iap_req.dart';

///按月订阅
const String _kMonthlySubscriptionId = 'wink_monthly';

///按年订阅
const String _kYearlySubscriptionId = 'wink_yearly';
const List<String> _kProductIds = <String>[
  _kMonthlySubscriptionId,
  _kYearlySubscriptionId,
];

class VipOpenEvent {
  VipOpenEvent(this.productId);

  String productId;
}

class IAPHelper {
  static IAPHelper? _singleton;

  factory IAPHelper() {
    _singleton ??= IAPHelper._();
    return _singleton!;
  }

  IAPHelper._();

  final InAppPurchase _inAppPurchaseInstance = InAppPurchase.instance;
  late StreamSubscription<List<PurchaseDetails>> _subscriptionPurchaseDetails;

  List<String> _notFoundProductIds = <String>[];

  List<ProductDetails> _productDetailsList = <ProductDetails>[];

  List<PurchaseDetails> _purchaseDetailsList = <PurchaseDetails>[];
  bool? _isIAPAvailable;

  ///是否显示loading
  bool _purchasePending = false;

  ///是否需要显示loading
  bool _isNeedShowLoadingDialog = false;

  ///订阅状态改变
  var subscribeStatus = 0.obs;

  ///订阅过期时间
  int? _subscribeExpiresDateMs;

  ///上一次检查订阅时的状态
  bool? _lastIsVip;

  bool isJumpPage = false;

  ///是否订阅成为VIP
  bool get isVip {
    bool result = false;
    if (_subscribeExpiresDateMs != null &&
        _subscribeExpiresDateMs! > DateTime.now().millisecondsSinceEpoch) {
      result = true;
    }
    //上一次订阅状态和当前订阅状态不一致时发出通知
    if (_lastIsVip != null && _lastIsVip != result) {
      Future.delayed(const Duration(milliseconds: 100)).then((value) {
        subscribeStatus.value = subscribeStatus.value + 1;
      });
    }
    if (!result && !isJumpPage) {
      isJumpPage = true;
      Future.delayed(const Duration(seconds: 1)).then((value) {
        PageJumpUtils.toVipPage(SubscribeFrom.start);
      });
    }
    _lastIsVip = result;
    return result;
  }

  ///查询产品错误原因
  String? _queryProductError;

  /// 异步方法并发锁
  Completer<void>? _syncLock;

  /// 防止异步方法并发
  Future<void> _sync(Future Function()? fn) async {
    // 设置同步等待
    var lastCompleter = _syncLock;
    var completer = Completer<void>();
    _syncLock = completer;
    // 等待其他同步任务完成
    await lastCompleter?.future;
    // 主任务
    await fn?.call();
    // 结束
    completer.complete();
  }

  Future<void> initIAP() async {
    await _sync(() async {
      if (_isIAPAvailable != true) {
        final bool isIAPAvailable = await _inAppPurchaseInstance.isAvailable();
        if (!isIAPAvailable) {
          return;
        }
        try {
          if (_isIAPAvailable != true) {
            _initListener();
          }
          if (_productDetailsList.isEmpty) {
            await _queryProduct();
          }
          _isIAPAvailable = isIAPAvailable;
        } catch (e) {
          _isIAPAvailable = false;
        }
      } else {
        if (_productDetailsList.isEmpty) {
          await _queryProduct();
        }
      }
    });
  }

  Future<void> _queryProduct() async {
    if (Platform.isIOS) {
      final InAppPurchaseStoreKitPlatformAddition iosPlatformAddition =
          _inAppPurchaseInstance
              .getPlatformAddition<InAppPurchaseStoreKitPlatformAddition>();
      await iosPlatformAddition.setDelegate(CustomPaymentQueueDelegate());
    }

    final ProductDetailsResponse productDetailResponse =
        await _inAppPurchaseInstance.queryProductDetails(_kProductIds.toSet());
    if (productDetailResponse.error != null) {
      _queryProductError = productDetailResponse.error!.message;
      _productDetailsList = productDetailResponse.productDetails;
      _productSort();
      _purchaseDetailsList = <PurchaseDetails>[];
      _notFoundProductIds = productDetailResponse.notFoundIDs;
      _setPurchasePending(false);
      return;
    }

    if (productDetailResponse.productDetails.isEmpty) {
      _queryProductError = null;
      _productDetailsList = productDetailResponse.productDetails;
      _productSort();
      _purchaseDetailsList = <PurchaseDetails>[];
      _notFoundProductIds = productDetailResponse.notFoundIDs;
      _setPurchasePending(false);
      return;
    }

    _productDetailsList = productDetailResponse.productDetails;
    _productSort();
    _notFoundProductIds = productDetailResponse.notFoundIDs;
    _setPurchasePending(false);
  }

  void _productSort() {
    _productDetailsList.sort((l, r) {
      if (r.id == _kMonthlySubscriptionId) {
        return 1;
      } else {
        return 0;
      }
    });
  }

  void _initListener() {
    final Stream<List<PurchaseDetails>> purchaseUpdated =
        _inAppPurchaseInstance.purchaseStream;
    _subscriptionPurchaseDetails =
        purchaseUpdated.listen((purchaseDetailsList) {
      _listenToPurchaseUpdated(purchaseDetailsList);
    }, onDone: () {
      _subscriptionPurchaseDetails.cancel();
    }, onError: (Object error) {
      // handle error here.
    });
  }

  Future<void> _listenToPurchaseUpdated(
      List<PurchaseDetails> purchaseDetailsList) async {
    if (purchaseDetailsList.isEmpty) {
      _handleErrorInfo(null);
      return;
    }
    List<PurchaseDetails>? pendingList;
    List<PurchaseDetails>? errorList;
    List<PurchaseDetails>? canceledList;
    List<PurchaseDetails>? restoredList;
    List<PurchaseDetails>? purchasedList;
    for (final PurchaseDetails purchaseDetails in purchaseDetailsList) {
      if (purchaseDetails.status == PurchaseStatus.pending) {
        pendingList ??= [];
        pendingList.add(purchaseDetails);
      } else if (purchaseDetails.status == PurchaseStatus.error) {
        errorList ??= [];
        errorList.add(purchaseDetails);
      } else if (purchaseDetails.status == PurchaseStatus.canceled) {
        canceledList ??= [];
        canceledList.add(purchaseDetails);
      } else if (purchaseDetails.status == PurchaseStatus.restored) {
        restoredList ??= [];
        restoredList.add(purchaseDetails);
      } else if (purchaseDetails.status == PurchaseStatus.purchased) {
        purchasedList ??= [];
        purchasedList.add(purchaseDetails);
      }
    }
    for (final PurchaseDetails purchaseDetails in purchaseDetailsList) {
      if (purchaseDetails.pendingCompletePurchase) {
        await _inAppPurchaseInstance.completePurchase(purchaseDetails);
      }
    }
    if (errorList?.isNotEmpty == true || canceledList?.isNotEmpty == true) {
      _handleErrorInfo(errorList?.firstOrNull?.error);
    } else if (pendingList?.isNotEmpty == true) {
      showPendingLoading();
    } else if (restoredList?.isNotEmpty == true ||
        purchasedList?.isNotEmpty == true) {
      checkSubscribeStatus(isNeedShowLoading: _isNeedShowLoadingDialog);
    }
  }

  void showPendingLoading() {
    _setPurchasePending(true);
  }

  Future<(bool, String, int?)> _verifyPurchaseStatus(String productId) async {
    if (Platform.isIOS) {
      try {
        var devicesId = await ValueHelper.instance.getDeviceId();
        var packageName = PackageInfoUtils.getPackageName();
        try {
          await SKRequestMaker().startRefreshReceiptRequest();
        } catch (e) {}
        var receipt = await SKReceiptManager.retrieveReceiptData();
        var params = AIAPReq()
          ..deviceId = devicesId
          ..packageName = packageName
          ..productId = productId
          ..receiptBase64Data = receipt;
        var result = await IAPApi().verifyApplePurchase(params);
        if (result.entity?.ok == true) {
          return (
            true,
            productId,
            result.entity?.latestReceiptInfo?.firstOrNull?.expiresDateMs
          );
        }
      } catch (e) {
        e.printInfo();
      }
    }
    return (false, productId, null);
  }

  Future<void> _refreshSubscribeStatus(
    String? productId,
    int? expiresDateMs,
  ) async {
    _subscribeExpiresDateMs = expiresDateMs;
    subscribeStatus.value = subscribeStatus.value + 1;
    if (expiresDateMs != null && productId != null) {
      eventBus.fire(VipOpenEvent(productId));
    }
  }

  GooglePlayPurchaseDetails? _getOldSubscriptionInfo(
      ProductDetails productDetails, Map<String, PurchaseDetails> purchases) {
    GooglePlayPurchaseDetails? oldSubscription;
    if (productDetails.id == _kMonthlySubscriptionId &&
        purchases[_kYearlySubscriptionId] != null) {
      oldSubscription =
          purchases[_kYearlySubscriptionId]! as GooglePlayPurchaseDetails;
    } else if (productDetails.id == _kYearlySubscriptionId &&
        purchases[_kMonthlySubscriptionId] != null) {
      oldSubscription =
          purchases[_kMonthlySubscriptionId]! as GooglePlayPurchaseDetails;
    }
    return oldSubscription;
  }

  Future<List<ProductDetails>?> getProducts() async {
    await initIAP();
    if (_isIAPAvailable == true) {
      return _productDetailsList;
    } else {
      toast('In app purchase not available');
      return null;
    }
  }

  Future<void> purchases(
    ProductDetails productDetails, {
    bool isNeedShowLoading = true,
  }) async {
    _isNeedShowLoadingDialog = isNeedShowLoading;
    await initIAP();
    if (_isIAPAvailable != true) {
      toast('In app purchase not available');
      return;
    }

    final Map<String, PurchaseDetails> purchases =
        Map<String, PurchaseDetails>.fromEntries(
            _purchaseDetailsList.map((PurchaseDetails purchase) {
      if (purchase.pendingCompletePurchase) {
        _inAppPurchaseInstance.completePurchase(purchase);
      }
      return MapEntry<String, PurchaseDetails>(purchase.productID, purchase);
    }));

    late PurchaseParam purchaseParam;
    if (Platform.isIOS) {
      var transactions = await SKPaymentQueueWrapper().transactions();
      if (transactions.isNotEmpty) {
        for (var skPaymentTransactionWrapper in transactions) {
          await SKPaymentQueueWrapper()
              .finishTransaction(skPaymentTransactionWrapper);
        }
      }
    }

    if (Platform.isAndroid) {
      final GooglePlayPurchaseDetails? oldSubscription =
          _getOldSubscriptionInfo(productDetails, purchases);

      purchaseParam = GooglePlayPurchaseParam(
          productDetails: productDetails,
          changeSubscriptionParam: (oldSubscription != null)
              ? ChangeSubscriptionParam(
                  oldPurchaseDetails: oldSubscription,
                  prorationMode: ProrationMode.immediateWithTimeProration,
                )
              : null);
    } else {
      purchaseParam = PurchaseParam(
        productDetails: productDetails,
      );
    }
    try {
      _inAppPurchaseInstance.buyNonConsumable(purchaseParam: purchaseParam);
    } catch (e) {}
  }

  Future<void> checkSubscribeStatus({bool isNeedShowLoading = false}) async {
    _isNeedShowLoadingDialog = isNeedShowLoading;
    await initIAP();
    if (_isIAPAvailable == true) {
      var results = await Future.wait<(bool, String, int?)>(
          _kProductIds.map((e) => _verifyPurchaseStatus(e)));
      var subscribeResult = results.firstWhereOrNull((element) => element.$1);
      var isSubscribe = subscribeResult?.$1;
      _setPurchasePending(false);
      if (isSubscribe == true) {
        _refreshSubscribeStatus(subscribeResult?.$2, subscribeResult?.$3);
      } else {
        _refreshSubscribeStatus(null, null);
      }
      if (_isNeedShowLoadingDialog) {
        toast(isSubscribe == true
            ? 'Subscription successful'
            : 'Subscription failed');
      }
    } else {
      _setPurchasePending(false);
      if (_isNeedShowLoadingDialog) {
        toast('This device does not support internal purchases');
      }
    }
  }

  ///恢复购买
  Future<void> restorePurchases({bool isNeedShowLoading = true}) async {
    _isNeedShowLoadingDialog = isNeedShowLoading;
    _setPurchasePending(true);
    await initIAP();
    await _inAppPurchaseInstance.restorePurchases();
  }

  void _handleErrorInfo(IAPError? error) {
    _setPurchasePending(false);
  }

  void _setPurchasePending(bool purchasePending) {
    if (purchasePending != _purchasePending) {
      _purchasePending = purchasePending;
      if (purchasePending) {
        if (_isNeedShowLoadingDialog) {
          showLoading();
        }
      } else {
        try {
          dismissLoading();
        } catch (e) {}
      }
    }
  }
}
